/* vim: set ts=2 et sw=2 cindent fo=qroca: */

package com.globant.google.mendoza.malbec;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.FieldPosition;
import java.util.Locale;

import javax.xml.bind.JAXBElement;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.globant.google.mendoza.malbec.schema._2.BackorderItemsRequest;
import com.globant.google.mendoza.malbec.schema._2.CancelItemsRequest;
import com.globant.google.mendoza.malbec.schema._2.ItemId;
import com.globant.google.mendoza.malbec.schema._2.ItemShippingInformation;
import com.globant.google.mendoza.malbec.schema._2.
OrderShippingInformationResponse;
import com.globant.google.mendoza.malbec.schema._2.
ResetItemsShippingInformationRequest;
import com.globant.google.mendoza.malbec.schema._2.ReturnItemsRequest;
import com.globant.google.mendoza.malbec.schema._2.TrackingData;
import com.globant.google.mendoza.malbec.schema._2.
ShipItemsRequest.ItemShippingInformationList;

import com.globant.google.mendoza.malbec.transport.Transport;

/** A GBuy order.
 */
// TODO Replace the wired html to objects and then use the converter to get the
// xml from it.
// TODO Replace String + with StringBuffer append.
public final class Order {

  /** The response xml namespace.
   */
  private static final String NAMESPACE = "http://checkout.google.com/schema/2";

  /** The class logger.
   */
  private static Log log = LogFactory.getLog(Order.class);

  /** The order number.
   */
  private String orderNumber = null;

  /** The transport implementation.
   */
  private Transport transport = null;

  /** The possible values for the carrier.
   */
  public enum Carrier {
    /** The DHL carrier. */
    DHL,
    /** Federal express carrier. */
    FedEx,
    /** The UPS carrier. */
    UPS,
    /** The USPS carrier. */
    USPS,
    /** Another unspecified carrier. */
    Other
  }

  /** Builds an order.
   *
   * @param theTransport The transport implementation. It cannot be null.
   *
   * @param number The GBuy order number. Cannot be null.
   */
  public Order(final Transport theTransport, final String number) {
    if (theTransport == null) {
      throw new IllegalArgumentException("the transport cannot be null");
    }
    if (number == null) {
      throw new IllegalArgumentException(
          "the order number cannot be null");
    }
    orderNumber = number;
    transport = theTransport;
  }

  /** Gets the GBuy order number.
   *
   * @return Returns the order number.
   */
  public String getNumber() {
    return orderNumber;
  }

  /** Attempts to charge the order for the full order amount.
   *
   * Possible invalid state errors for charge-order: the order is not in a
   * financial order state that can be charged.
   */
  public void charge() {
    log.trace("Entering charge");

    String xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        + "<charge-order xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\"/>\n";

    send(xmlMessage);
    log.trace("Leaving charge");
  }

  /** Attempts to charge the order for a partial order amount.
   *
   * Possible invalid argument errors for charge-order: requested amount is
   * greater than remaining available amount, requested amount is zero or
   * negative, requested amount has too many fractional digits.
   *
   * Possible invalid state errors for charge-order: the order is not in a
   * financial order state that can be charged.
   *
   * @todo Add the currency (USD) as a parameter to charge.
   *
   * @param amount The amount to charge the order.
   */
  public void charge(final BigDecimal amount) {
    log.trace("Entering charge");

    StringBuffer formattedAmount = new StringBuffer();

    DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US);
    DecimalFormat format = new DecimalFormat("0.00", symbols);
    format.format(amount, formattedAmount, new FieldPosition(0));

    String xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        + "<charge-order xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\">\n"
        + " <amount currency=\"USD\">" + formattedAmount.toString()
        + "</amount>\n" + "</charge-order>\n";

    send(xmlMessage);
    log.trace("Leaving charge");
  }

  /** Refunds the buyer the full amount of the order.
   *
   * Possible invalid state errors for refund-order: the order is not in a
   * financial order state that can be refunded.
   *
   * @param reason The reason the refund is made. It cannot be null.
   *
   * @param comment An additional comment. It can be null, in with case no
   * comment is sent to the buyer.
   */
  public void refund(final String reason, final String comment) {
    log.trace("Entering refund");
    if (reason == null) {
      throw new IllegalArgumentException("the reason cannot be null");
    }
    String xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        + "<refund-order xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\">\n";
    if (comment != null) {
      xmlMessage += " <comment>" + comment + "</comment>\n";
    }
    xmlMessage += " <reason>" + reason + "</reason>\n"
        + "</refund-order>\n";

    send(xmlMessage);
    log.trace("Leaving refund");
  }

  /** Refunds the buyer a partial amount of the order.
   *
   * Possible invalid argument errors for refund-order: requested amount is
   * greater than remaining available amount, requested amount is zero or
   * negative, requested amount has too many fractional digits.
   *
   * Possible invalid state errors for refund-order: the order is not in a
   * financial order state that can be refunded.
   *
   * @param amount The amount to refund.
   *
   * @param reason The reason the refund is made. It cannot be null.
   *
   * @param comment An additional comment. It can be null, in with case no
   * comment is sent to the buyer.
   */
  public void refund(final BigDecimal amount, final String reason,
      final String comment) {
    log.trace("Entering refund");
    if (reason == null) {
      throw new IllegalArgumentException("the reason cannot be null");
    }

    StringBuffer formattedAmount = new StringBuffer();
    DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US);
    DecimalFormat format = new DecimalFormat("0.00", symbols);
    format.format(amount, formattedAmount, new FieldPosition(0));

    String xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        + "<refund-order xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\">\n"
        + " <amount currency=\"USD\">" + formattedAmount.toString()
        + "</amount>\n";
    if (comment != null) {
      xmlMessage += " <comment>" + comment + "</comment>\n";
    }
    xmlMessage += " <reason>" + reason + "</reason>\n"
        + "</refund-order>\n";

    send(xmlMessage);
    log.trace("Leaving refund");
  }

  /** Cancels the order. If the order has been charged, you must refund it
   * before you can cancel it.
   *
   * Possible invalid state errors for cancel-order: the order is not in a
   * financial order state that can be cancelled.
   *
   * @param reason The reason the order was cancelled. Cannot be null.
   *
   * @param comment An optional comment. Note that this comment will be visible
   * to the buyer in the order history. It can be null.
   */
  public void cancel(final String reason, final String comment) {
    log.trace("Entering cancel");

    if (reason == null) {
      throw new IllegalArgumentException("the reason cannot be null");
    }
    String xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        + "<cancel-order xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\">\n";
    if (comment != null) {
      xmlMessage += " <comment>" + comment + "</comment>\n";
    }
    xmlMessage += " <reason>" + reason + "</reason>\n"
        + "</cancel-order>\n";

    send(xmlMessage);
    log.trace("Leaving cancel");
  }

  /** Notifies the buyer that the order is being processed.
   *
   * Adds a note to the buyer's inbox that says you're processing the order.
   */
  public void process() {
    log.trace("Entering process");

    String xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        + "<process-order xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\"/>\n";

    send(xmlMessage);
    log.trace("Leaving process");
  }

  /** Notifies the buyer that the order has been shipped.
   *
   * @param sendEmail States if GBuy must send a mail to the buyer (true) or
   * not (false).
   */
  public void deliver(final boolean sendEmail) {
    log.trace("Entering deliver");
    String xmlMessage;
    xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        + "<deliver-order xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\">\n";
    if (!sendEmail) {
      xmlMessage += " <send-email>false</send-email>\n";
    }
    xmlMessage += "</deliver-order>\n";
    send(xmlMessage);
    log.trace("Leaving deliver");
  }

  /** Notifies the buyer that the order has been shipped.
   *
   * @param carrier The name of the carrier. See the Carrier enumerator for
   * possible values. Cannot be null.
   *
   * @param trackingNumber The assigned tracking number for this order
   * shipment. It cannot be null.
   *
   * @param sendEmail States if GBuy must send a mail to the buyer (true) or
   * not (false).
   */
  public void deliver(final Carrier carrier, final String trackingNumber,
      final boolean sendEmail) {
    log.trace("Entering deliver");
    if (carrier == null) {
      throw new IllegalArgumentException("the carrier cannot be null");
    }
    if (trackingNumber == null) {
      throw new IllegalArgumentException(
          "the tracking number cannot be null");
    }
    String xmlMessage;
    xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        + "<deliver-order xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\">\n"
        + " <tracking-data>\n" + "  <carrier>" + carrier
        + "</carrier>\n" + "  <tracking-number>" + trackingNumber
        + "</tracking-number>\n" + " </tracking-data>\n";
    if (!sendEmail) {
      xmlMessage += " <send-email>false</send-email>\n";
    }
    xmlMessage += "</deliver-order>\n";
    send(xmlMessage);
    log.trace("Leaving deliver");
  }

  /** Adds tracking data to a shipped order.
   *
   * Add a shipping tracking number to this order. Be sure to do this when you
   * have tracking information, as stated in GBuy Program Policies and
   * Guidelines, section 1d.
   *
   * @param carrier The name of the carrier. See the Carrier enumerator for
   * possible values. Cannot be null.
   *
   * @param trackingNumber The assigned tracking number for this order
   * shipment. It cannot be null.
   */
  public void addTrackingData(final Carrier carrier,
      final String trackingNumber) {
    log.trace("Entering addTrackingData");
    if (carrier == null) {
      throw new IllegalArgumentException("the carrier cannot be null");
    }
    if (trackingNumber == null) {
      throw new IllegalArgumentException(
          "the tracking number cannot be null");
    }
    String xmlMessage;
    xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        + "<add-tracking-data xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\">\n"
        + " <tracking-data>\n" + "  <carrier>" + carrier
        + "</carrier>\n" + "  <tracking-number>" + trackingNumber
        + "</tracking-number>\n" + " </tracking-data>\n"
        + "</add-tracking-data>\n";
    send(xmlMessage);
    log.trace("Leaving addTrackingData");
  }

  /** Adds the merchant order number to this order.
   *
   * @param merchantOrderNumber The merchant order number to associate to this
   * order. It cannot be null.
   */
  public void addMerchantOrderNumber(final String merchantOrderNumber) {
    log.trace("Entering addMerchantOrderNumber");
    if (merchantOrderNumber == null) {
      throw new IllegalArgumentException(
          "the merchant order number cannot be" + " null");
    }
    String xmlMessage;
    xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        + "<add-merchant-order-number xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\">\n"
        + " <merchant-order-number>" + merchantOrderNumber
        + "</merchant-order-number>\n"
        + "</add-merchant-order-number>\n";
    send(xmlMessage);
    log.trace("Leaving addMerchantOrderNumber");
  }

  /** Place a message in the buyer's account and optionally send the message
   * via email.
   *
   * @param message The message to send to the buyer. It cannot be null.
   *
   * @param sendEmail If true, this also sends a mail to the buyer.
   */
  public void sendBuyerMessage(final String message, final boolean sendEmail) {
    log.trace("Entering sendBuyerMessage");
    if (message == null) {
      throw new IllegalArgumentException("the message cannot be null");
    }
    String xmlMessage;
    xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        + "<send-buyer-message xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\">\n";
    if (sendEmail) {
      xmlMessage += " <send-email>true</send-email>\n";
    }
    xmlMessage += " <message>" + message + "</message>\n"
        + "</send-buyer-message>\n";
    send(xmlMessage);
    log.trace("Leaving sendBuyerMessage");
  }

  /** Request the order shipping information to Checkout.
   * @return The shipping information response.
   */
  public OrderShippingInformationResponse getOrderShippingInformation() {
    log.trace("Entering getOrderShipppingInformation");
    String xmlMessage;
    xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
      + "<get-order-shipping-information xmlns=\"" + NAMESPACE
      + "\" google-order-number=\"" + orderNumber + "\"/>\n";
    Object response = sendAndReceive(xmlMessage);
    if (!(response instanceof OrderShippingInformationResponse)) {
      throw new RuntimeException(
          "Unexpected response after sending a"
          + " get-order-shipping-information message");
    }
    log.trace("Leaving getOrderShipppingInformation");
    return (OrderShippingInformationResponse) response;
  }

  /** Restores the given order to your Merchant Account inbox.
   */
  public void unArchive() {
    log.trace("Entering unArchive");
    String xmlMessage;
    xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        + "<unarchive-order xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\"/>\n";
    send(xmlMessage);
    log.trace("Leaving unArchive");
  }

 /** Adds information of the shipment in a per item way.
  *
  * @param itemInformation The shipping information of every item, includes
  *  item-id, carrier and tracking-number. Cannot be neither null nor empty
  *
  * @param sendEmail States if GBuy must send a mail to the buyer (true) or
  * not (false).
  */
 public void shipItems(final ItemShippingInformationList itemInformation,
     final boolean sendEmail) {
   log.trace("Entering shipItems");
   if (itemInformation == null) {
     throw new IllegalArgumentException("the itemInformation cannot be null");
   }
   if (itemInformation.getItemShippingInformation().isEmpty()) {
     throw new IllegalArgumentException(
         "the itemInformation list cannot be empty");
   }

   String xmlMessage;

   xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
       + "<ship-items xmlns=\"" + NAMESPACE
       + "\" google-order-number=\"" + orderNumber + "\">\n"
       + " <item-shipping-information-list>\n";

   for (ItemShippingInformation item
       : itemInformation.getItemShippingInformation()) {

     xmlMessage += " <item-shipping-information>\n"
     + " <item-id>\n";
     if (item.getItemId().getMerchantItemId() != null) {
       xmlMessage += " <merchant-item-id>"
       + item.getItemId().getMerchantItemId()
       + "</merchant-item-id>\n";
     }
     if (item.getItemId().getGoogleItemId() != null) {
       xmlMessage += "<google-item-id>" + item.getItemId().getGoogleItemId()
       + "</google-item-id>\n";
     }
     xmlMessage += "</item-id>\n"
     + "<tracking-data-list>\n";

     for (TrackingData trackingData
         : item.getTrackingDataList().getTrackingData()) {

       xmlMessage += "<tracking-data>\n"
       + "<carrier>" + trackingData.getCarrier() + "</carrier>\n"
       + "<tracking-number>" + trackingData.getTrackingNumber()
       + "</tracking-number>\n"
       + "</tracking-data>\n";
     }
     xmlMessage += "</tracking-data-list>\n" + "</item-shipping-information>\n";
   }

   xmlMessage += " </item-shipping-information-list>\n";
   if (!sendEmail) {
     xmlMessage += " <send-email>false</send-email>\n";
   }
   xmlMessage += "</ship-items>\n";

   send(xmlMessage);
   log.trace("Leaving shipItems");
 }

  /** Cancel the indicated items.
  *
  * @param itemIds The item ids that needs to be cancel.
  *
  * @param sendEmail States if GBuy must send a mail to the buyer (true) or
  * not (false).
  */
  public void cancelItems(final CancelItemsRequest.ItemIds itemIds,
    final boolean sendEmail) {
    log.trace("Entering cancelItems");
    if (itemIds == null) {
      throw new IllegalArgumentException("the itemInformation cannot be null");
    }
    if (itemIds.getItemId().isEmpty()) {
      throw new IllegalArgumentException(
        "the itemIds list cannot be empty");
    }

    String xmlMessage;

    xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
       + "<cancel-items xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\">\n"
        + " <item-ids>\n";

    for (ItemId item : itemIds.getItemId()) {

      xmlMessage += " <item-id>\n";
      if (item.getMerchantItemId() != null) {
        xmlMessage += " <merchant-item-id>"
        + item.getMerchantItemId()
        + "</merchant-item-id>\n";
      }
      if (item.getGoogleItemId() != null) {
        xmlMessage += "<google-item-id>" + item.getGoogleItemId()
        + "</google-item-id>\n";
      }
      xmlMessage += "</item-id>\n";
    }
    xmlMessage += "</item-ids>\n";

    if (!sendEmail) {
      xmlMessage += " <send-email>false</send-email>\n";
    }
    xmlMessage += "</cancel-items>\n";

    send(xmlMessage);
    log.trace("Leaving cancelItems");
  }

  /** Returns the items.
  *
  * @param itemIds the item ids that need to be returned.
  *
  * @param sendEmail States if GBuy must send a mail to the buyer (true) or
  * not (false).
  */
  public void returnItems(final ReturnItemsRequest.ItemIds itemIds,
    final boolean sendEmail) {
    log.trace("Entering returnItems");
    if (itemIds == null) {
      throw new IllegalArgumentException("the itemInformation cannot be null");
    }
    if (itemIds.getItemId().isEmpty()) {
      throw new IllegalArgumentException("the itemIds list cannot be empty");
    }

    String xmlMessage;

    xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
       + "<return-items xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\">\n"
        + " <item-ids>\n";

    for (ItemId item : itemIds.getItemId()) {

      xmlMessage += " <item-id>\n";
      if (item.getMerchantItemId() != null) {
        xmlMessage += " <merchant-item-id>"
        + item.getMerchantItemId()
        + "</merchant-item-id>\n";
      }
      if (item.getGoogleItemId() != null) {
        xmlMessage += "<google-item-id>" + item.getGoogleItemId()
        + "</google-item-id>\n";
      }
      xmlMessage += "</item-id>\n";
    }
    xmlMessage += "</item-ids>\n";

    if (!sendEmail) {
      xmlMessage += " <send-email>false</send-email>\n";
    }
    xmlMessage += "</return-items>\n";

    send(xmlMessage);
    log.trace("Leaving returnItems");
  }

  /** Backorder the indicated items.
  *
  * @param itemIds The item ids to be backordered.
  *
  * @param sendEmail States if GBuy must send a mail to the buyer (true) or
  * not (false).
  */
  public void backorderItems(final BackorderItemsRequest.ItemIds itemIds,
    final boolean sendEmail) {
    log.trace("Entering backorderItems");
    if (itemIds == null) {
      throw new IllegalArgumentException("the itemInformation cannot be null");
    }
    if (itemIds.getItemId().isEmpty()) {
      throw new IllegalArgumentException(
        "the itemIds list cannot be empty");
    }
    String xmlMessage;

    xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
       + "<backorder-items xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\">\n"
        + " <item-ids>\n";

    for (ItemId item : itemIds.getItemId()) {

      xmlMessage += " <item-id>\n";
      if (item.getMerchantItemId() != null) {
        xmlMessage += " <merchant-item-id>"
        + item.getMerchantItemId()
        + "</merchant-item-id>\n";
      }
      if (item.getGoogleItemId() != null) {
        xmlMessage += "<google-item-id>" + item.getGoogleItemId()
        + "</google-item-id>\n";
      }
      xmlMessage += "</item-id>\n";
    }
    xmlMessage += "</item-ids>\n";

    if (!sendEmail) {
      xmlMessage += " <send-email>false</send-email>\n";
    }
    xmlMessage += "</backorder-items>\n";

    send(xmlMessage);
    log.trace("Leaving backorderItems");
  }

  /** Reset the indicated items.
  *
  * @param itemIds The item ids needed to be reset.
  *
  * @param sendEmail States if GBuy must send a mail to the buyer (true) or
  * not (false).
  */
  public void resetItems(
      final ResetItemsShippingInformationRequest.ItemIds itemIds,
      final boolean sendEmail) {
    log.trace("Entering resetItems");
    if (itemIds == null) {
      throw new IllegalArgumentException("the itemInformation cannot be null");
    }
    if (itemIds.getItemId().isEmpty()) {
      throw new IllegalArgumentException(
        "the itemIds list cannot be empty");
    }

    String xmlMessage;

    xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
       + "<reset-items-shipping-information xmlns=\"" + NAMESPACE
        + "\" google-order-number=\"" + orderNumber + "\">\n"
        + " <item-ids>\n";

    for (ItemId item : itemIds.getItemId()) {

      xmlMessage += " <item-id>\n";
      if (item.getMerchantItemId() != null) {
        xmlMessage += " <merchant-item-id>"
        + item.getMerchantItemId()
        + "</merchant-item-id>\n";
      }
      if (item.getGoogleItemId() != null) {
        xmlMessage += "<google-item-id>" + item.getGoogleItemId()
        + "</google-item-id>\n";
      }
      xmlMessage += "</item-id>\n";
    }
    xmlMessage += "</item-ids>\n";

    if (!sendEmail) {
      xmlMessage += " <send-email>false</send-email>\n";
    }
    xmlMessage += "</reset-items-shipping-information>\n";

    send(xmlMessage);
    log.trace("Leaving resetItems");
  }

 /** Archives this order, removing it from your Merchant Account inbox.
  */
 public void archive() {
   log.trace("Entering archive");
   String xmlMessage;
   xmlMessage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
       + "<archive-order xmlns=\"" + NAMESPACE
       + "\" google-order-number=\"" + orderNumber + "\"/>\n";
   send(xmlMessage);
   log.trace("Leaving archive");
 }

  /** Compares two order objects.
   *
   * It only takes into account the order number.
   *
   * @param other The object to compare to.
   *
   * @return Returs true if both orders contain the same order number.
   */
  public boolean equals(final Object other) {
    if (this == other) {
      return true;
    }
    if ((other == null) || (other.getClass() != getClass())) {
      return false;
    }
    Order second = (Order) other;
    return orderNumber.equals(second.orderNumber);
  }

  /** Return the hash code.
   *
   * @return Returns the hash code.
   */
  public int hashCode() {
    return orderNumber.hashCode();
  }

  /** Sends a message to the GBuy server corresponding to one of the order
   * commands.
   *
   * Throws an exception if the response is not the expected one.
   *
   * This implementation simply searches for the string request-received and if
   * not found it throws an exception.
   *
   * @param message The message to send to GBuy.
   */
  public void send(final String message) {
    String response = null;
    response = transport.send(message);

    // This is a very naive check to verify if the command was sucessful. It is
    // compatible with XML and name/value.
    if (-1 == response.indexOf("request-received")) {
      throw new RuntimeException("Unexpected response from GBuy: "
          + response);
    }
  }

  /** Sends a message to the GBuy server corresponding to one of the order
   * commands.
   *
   * Throws an exception if the response is not the expected one.
   *
   * This implementation simply searches for the string request-received and if
   * not found it throws an exception.
   *
   * @param message The message to send to GBuy.
   * @return the response from Checkout.
   */
  public Object sendAndReceive(final String message) {
    String response = null;
    response = transport.send(message);

    JAXBElement node = CartUtils.unmarshal(response);
    return node.getValue();
  }
}

